package com.ruoyi.game.core.card;

import com.ruoyi.game.core.*;
import com.ruoyi.game.core.exception.GameOpException;
import com.ruoyi.game.core.status.LiangZhuInfo;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;


import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.ruoyi.game.core.card.BasePoker.*;import com.ruoyi.game.constant.GameErrorCode;

@Slf4j
@Data
public class CardTypeChecker {

    /**
     * 校验出牌的合法性
     * @param pos 当前玩家的游戏状态
     * @param opType 当前操作类型
     * @param cards 玩家出牌的卡牌
     * @param set 当前的游戏回合状态
     * @return 是否合法
     */
    public boolean check(GameSetPos pos, PKOpType opType, List<Integer> cards, GameSetCore set) {
        log.info("[CardTypeChecker] 检查操作类型 opType={} 当前状态={}", opType, set.getState());

        switch (set.getState()) {
            case CALL_ZHU_STATE -> {
                return checkCallZhuOp(pos, opType, cards, set);
            }
            case KOU_DI_STATE -> {
                return checkKouDiOp(pos, opType, cards, set);
            }
            case HAN_DA_STATE -> {
                return checkHandaOp(pos, opType, cards, set);
            }
            case PLAYING_STATE -> {
                return checkPlayingOp(pos, opType, cards, set);
            }
            default -> {
                log.warn("[CardTypeChecker] 未处理的对局状态：{}", set.getState());
                return false;
            }
        }
    }

    public boolean checkKouDiOp(GameSetPos pos, PKOpType opType, List<Integer> cards, GameSetCore set) {
        // 校验：卡牌数量必须是8张
        if (cards == null || cards.size() != 8) {
            log.warn("[GameSetCore] 扣底阶段，玩家 {} 选择的卡牌数量不为 8 张：{}", pos.getPlayerId(), cards!=null?cards.size():0);
            throw new GameOpException(GameErrorCode.INVALID_CARD, "扣底必须是8张牌");
        }

        // 校验：所有卡牌必须是玩家手牌中的卡牌
        if (!pos.getHandCards().containsAll(cards)) {
            log.warn("[GameSetCore] 扣底阶段，玩家 {} 选择的卡牌不在其手牌中，选择的卡牌：{}", pos.getPlayerId(), cards);
            throw new GameOpException(GameErrorCode.INVALID_CARD, "扣底必须是手牌");
        }

        log.info("[GameSetCore] 扣底校验通过，玩家 {} 选择的卡牌：{}", pos.getPlayerId(),
                cards.stream().map(BasePoker::getCardDescription).collect(Collectors.joining(", ")));

        return true;
    }

    // 校验抢庄阶段的操作
    private boolean checkCallZhuOp(GameSetPos pos, PKOpType opType, List<Integer> cards, GameSetCore set) {
        log.info("[CardTypeChecker] 校验抢庄阶段操作，opType={}", opType);

        // 针对亮主操作进行校验（根据手牌中是否有指定牌型）
        if (opType == PKOpType.LIANG_ZHU1 || opType == PKOpType.LIANG_ZHU2) {
            return checkLiangZhu(pos, cards, set);
        }

        // 如果是反主操作，检查是否有合适的牌来反主
        if (opType == PKOpType.FAN_ZHU) {
            return checkFanZhu(pos, cards, set);
        }

        // 如果是自保操作，检查是否符合自保规则
        if (opType == PKOpType.ZI_BAO) {
            return checkZiBao(pos, cards, set);
        }

        // 如果是自反操作，检查是否符合自反规则
        if (opType == PKOpType.ZI_FAN) {
            return checkZiFan(pos, cards, set);
        }

        return false; // 默认返回不合法
    }

    public boolean checkHandaOp(GameSetPos pos, PKOpType opType, List<Integer> cards, GameSetCore set) {
        if (opType == PKOpType.DA_DU) {
            return true;
        }

        // 必须选择一张牌
        if (CollectionUtils.isEmpty(cards) || cards.size() != 1) {
            log.error("[GameSetCore] 玩家 {} 喊打操作失败：必须选择且仅能选择一张牌，当前选中数量：{}", pos.getPlayerId(), cards == null ? 0 : cards.size());
            throw new GameOpException(GameErrorCode.INVALID_CARD, "喊打只能选1张牌");
        }

        int selectedCard = cards.get(0);
        int selectedValue = BasePoker.getCardValueEx(selectedCard);
        int selectedColor = BasePoker.getCardColor(selectedCard);

        log.info("[GameSetCore] 玩家 {} 喊打操作：检查牌值 = {}, 花色 = {}", pos.getPlayerId(), selectedValue, selectedColor);

        // 统计玩家手牌中，数值和花色相同的牌数量
        // 合并手牌和底牌后判断牌的总数
        List<Integer> fullCards = new ArrayList<>(pos.getHandCards());
        if (CollectionUtils.isNotEmpty(set.getKouPaiList())) {
            fullCards.addAll(set.getKouPaiList());
        }

        long count = fullCards.stream()
                .filter(card -> BasePoker.getCardValueEx(card) == selectedValue && BasePoker.getCardColor(card) == selectedColor)
                .count();

        if (count > 1) {
            log.error("[GameSetCore] 玩家 {} 喊打操作失败：选中的牌花色 {} 数字 {} 在手牌或底牌中出现了 {} 次", pos.getPlayerId(), selectedColor, selectedValue, count);
            throw new GameOpException(GameErrorCode.INVALID_CARD, "喊打牌在手牌或底牌中不能超过1张");
        }

        log.info("[GameSetCore] 玩家 {} 喊打操作通过，选中的牌：{}", pos.getPlayerId(), BasePoker.getCardDescription(selectedCard));
        return true;
    }

    /**
     * 校验亮主/反主等结构是否合法，不合法时直接抛出异常。
     * - 两张牌：必须同花色
     * - 三张牌：如包含两个以上王牌，必须全是大王或全是小王
     *
     * @param cards 出牌列表
     * @throws GameOpException 结构不合法时抛出
     */
    public static void validateLiangZhuStructureOrThrow(List<Integer> cards, int zhuValue) {
        if (cards == null || cards.isEmpty()) {
            throw new GameOpException(GameErrorCode.INVALID_CARD, "牌数不能为空");
        }

        if (cards.size() == 2) {
            int color0 = BasePoker.getCardColor(cards.get(0));
            int color1 = BasePoker.getCardColor(cards.get(1));

            if (color0 != color1) {
                log.warn("[亮主结构] 两张牌花色不一致：{} vs {}", BasePoker.getCardDescription(cards.get(0)), BasePoker.getCardDescription(cards.get(1)));
                throw new GameOpException(GameErrorCode.INVALID_CARD, "两张牌亮主/反主必须同花色");
            }

            boolean allJokers = cards.stream().allMatch(BasePoker::isJoker);
            if (allJokers) {
                log.warn("[亮主结构] 两张牌不能全部是王：{}", BasePoker.toCardString(cards));
                throw new GameOpException(GameErrorCode.INVALID_CARD, "两张王不能直接亮主，必须加2");
            }
        }

        if (cards.size() == 3) {
            List<Integer> jokerCards = cards.stream().filter(BasePoker::isJoker).toList();
            if (jokerCards.size() >= 2) {
                boolean allDaWang = jokerCards.stream().allMatch(BasePoker::isDaWang);
                boolean allXiaoWang = jokerCards.stream().allMatch(BasePoker::isXiaoWang);
                if (!(allDaWang || allXiaoWang)) {
                    log.warn("[亮主结构] 三张牌中，包含混合王：{}", BasePoker.toCardString(jokerCards));
                    throw new GameOpException(GameErrorCode.INVALID_CARD, "三张牌中，两个王必须同为大王或小王");
                }

                // ✅ 补充校验：非王牌必须是主牌
                Integer other = cards.stream().filter(c -> !BasePoker.isJoker(c)).findFirst().orElse(null);
                if (other == null) {
                    log.warn("[亮主结构] 三张牌中没有非王牌：{}", BasePoker.toCardString(cards));
                    throw new GameOpException(GameErrorCode.INVALID_CARD, "三张王不允许亮主");
                }

                int cardValue = BasePoker.getCardValue(other); // 假设这是获取点数的方法
                if (cardValue != zhuValue) {
                    log.warn("[亮主结构] 第三张非王牌不是主牌点数 (zhuValue={}): {}", zhuValue, BasePoker.getCardDescription(other));
                    throw new GameOpException(GameErrorCode.INVALID_CARD, "三张亮主中，非王牌必须为主牌点数");
                }
            }
            else {
                log.warn("[亮主结构] 3张牌亮主必须有2张王");
                throw new GameOpException(GameErrorCode.INVALID_CARD, "三张亮主中，必须有两张王");
            }
        }
    }

    // 校验亮主操作
    public boolean checkLiangZhu(GameSetPos pos, List<Integer> cards, GameSetCore set) {
        int liangZhuCount = cards.size();
        if (liangZhuCount < 1 || liangZhuCount > 3) {
            log.warn("[CardTypeChecker] 亮主操作牌数不合法，cards={}", cards);
            throw new GameOpException(GameErrorCode.INVALID_CARD, "亮主操作牌数必须是 1、2或3 张");
        }

        validateLiangZhuStructureOrThrow(cards, set.getZhuValue());

        // 检查场中是否已经有亮主牌，如果有，则无法再次亮主
        LiangZhuInfo currentLiangZhuInfo = set.getLiangZhuInfo(); // 获取第一次亮主信息，用于确定庄家
        if (currentLiangZhuInfo != null && !currentLiangZhuInfo.getZhuCards().isEmpty()) {
            log.warn("[CardTypeChecker] 场中已亮主，无法再次亮主");
            throw new GameOpException(GameErrorCode.ALREADY_LIANGZHU, "场中已亮主，无法再次亮主");
        }

        // 检查亮主的牌是否符合要求，例如检查是否有指定的花色和值
        for (Integer card : cards) {
            if (!isZhuPai(card, -1, set.getZhuValue())) {
                log.warn("[CardTypeChecker] 亮主牌不符合要求，card={}，zhuColor={}, zhuValue={}",
                        toCardString(card), set.getZhuValue());
                throw new GameOpException(GameErrorCode.INVALID_CARD, "亮主牌不是主牌");
            }
        }

        return true; // 合法
    }

    // 校验反主操作
    public boolean checkFanZhu(GameSetPos pos, List<Integer> cards, GameSetCore set) {
        int fanzhuZhuCount = cards.size();
        if (fanzhuZhuCount < 2 || fanzhuZhuCount > 3) {
            log.warn("[CardTypeChecker] 反主操作牌数不合法，cards={}", toCardString(cards));
            throw new GameOpException(GameErrorCode.INVALID_CARD, "反主操作牌数为2或者3张牌");
        }

        validateLiangZhuStructureOrThrow(cards, set.getZhuValue());

        // 反主操作时需要检查当前亮主的牌的强度
        LiangZhuInfo currentLiangZhuInfo = set.getLiangZhuInfo(); // 获取第一次亮主信息，用于确定庄家
        if (currentLiangZhuInfo == null || currentLiangZhuInfo.getZhuCards().isEmpty()) {
            log.warn("[CardTypeChecker] 当前没有亮主，无法进行反主操作");
            throw new GameOpException(GameErrorCode.INVALID_CARD, "本局还未亮主，无法反主");
        }

        // 计算当前玩家反主的牌的强度
        int liangZhuStrength = evaluateLiangZhu(cards, set);
        int currentLiangZhuStrength = evaluateLiangZhu(currentLiangZhuInfo.getZhuCards(), set);

        // 如果当前玩家的反主牌没有强过已亮主的牌，则返回不合法
        if (liangZhuStrength <= currentLiangZhuStrength) {
            log.warn("[CardTypeChecker] 当前反主牌不够强，liangZhuStrength={}，currentLiangZhuStrength={}",
                    liangZhuStrength, currentLiangZhuStrength);
            throw new GameOpException(GameErrorCode.INVALID_CARD, "反主牌型比当前已亮主小，反主失败");
        }

        return true; // 合法
    }

    // 校验自保操作
    public boolean checkZiBao(GameSetPos pos, List<Integer> cards, GameSetCore set) {
        if (cards == null || cards.isEmpty()) {
            throw new GameOpException(GameErrorCode.INVALID_CARD, "自保牌不能为空");
        }

        int zibaoCardCount = cards.size();
        if (zibaoCardCount != 1 && zibaoCardCount != 2) {
            log.warn("[CardTypeChecker] 自保操作牌数非法，cards={}", BasePoker.toCardString(cards));
            throw new GameOpException(GameErrorCode.INVALID_CARD, "自保操作只能出1张或2张牌");
        }

        LiangZhuInfo myLiangZhuInfo = pos.getLiangZhuInfo(); // ✅ 获取玩家自己的亮主信息
        if (myLiangZhuInfo == null || myLiangZhuInfo.getZhuCards().isEmpty()) {
            throw new GameOpException(GameErrorCode.INVALID_CARD, "你还未亮主，无法自保");
        }

        if (myLiangZhuInfo.getZhuCards().size() > 1) {
            log.warn("[CardTypeChecker] 玩家已亮 {} 张主牌，无法自保", myLiangZhuInfo.getZhuCards().size());
            throw new GameOpException(GameErrorCode.INVALID_CARD, "你已亮出多张主牌，无法自保");
        }

        int zhuCard = myLiangZhuInfo.getZhuCards().get(0);
        int zhuColor = BasePoker.getCardColor(zhuCard);
        int zhuValue = BasePoker.getCardValue(zhuCard);

        if (zibaoCardCount == 1) {
            int card = cards.get(0);
            if (BasePoker.isJoker(card)) {
                throw new GameOpException(GameErrorCode.INVALID_CARD, "自保打单张王牌非法，单张只能是同花色2");
            }
            int cardValue = BasePoker.getCardValue(card);
            int cardColor = BasePoker.getCardColor(card);
            if (cardValue != zhuValue || cardColor != zhuColor) {
                log.warn("[CardTypeChecker] 自保打出的单牌不符合要求，应为 zhuValue={} 且花色={}，实际={} {}",
                        zhuValue, zhuColor, cardValue, cardColor);
                throw new GameOpException(GameErrorCode.INVALID_CARD, "自保必须与已亮主的2同花色");
            }
        } else if (zibaoCardCount == 2) {
            boolean allDaWang = cards.stream().allMatch(BasePoker::isDaWang);
            boolean allXiaoWang = cards.stream().allMatch(BasePoker::isXiaoWang);
            if (!(allDaWang || allXiaoWang)) {
                log.warn("[CardTypeChecker] 自保出两张牌不是同一类王，cards={}", BasePoker.toCardString(cards));
                throw new GameOpException(GameErrorCode.INVALID_CARD, "自保打两张牌必须是两个大王或两个小王");
            }
        }

        return true;
    }


    // 校验自反操作
    public boolean checkZiFan(GameSetPos pos, List<Integer> cards, GameSetCore set) {
        int ziFanZhuCount = cards.size();
        if (ziFanZhuCount < 2 || ziFanZhuCount > 3) {
            log.warn("[CardTypeChecker] 自保操作牌数不合法，cards={}", toCardString(cards));
            throw new GameOpException(GameErrorCode.INVALID_CARD, "自保操作牌数为2或者3张牌");
        }

        validateLiangZhuStructureOrThrow(cards, set.getZhuValue());

        LiangZhuInfo currentLiangZhuInfo = set.getLiangZhuInfo(); // 获取第一次亮主信息，用于确定庄家
        if (currentLiangZhuInfo == null || currentLiangZhuInfo.getZhuCards().isEmpty()) {
            log.warn("[CardTypeChecker] 当前没有亮主，无法进行自反操作");
            throw new GameOpException(GameErrorCode.INVALID_CARD, "本局还未亮主，无法自反");
        }

        // 获取亮主的牌的花色
        int currentLiangZhuColor = BasePoker.getCardColor(currentLiangZhuInfo.getZhuCards().get(0));

        int mySeat = pos.getSeatIndex();
        int liangZhuSeat = currentLiangZhuInfo.getSeatIndex();

        if (mySeat != liangZhuSeat) {
            log.warn("[CardTypeChecker] 玩家 {} 不是当前亮主者 seat={}，无法执行自反/自保", mySeat, liangZhuSeat);
            throw new GameOpException(GameErrorCode.INVALID_CARD, "你不是当前亮主玩家，无法自反/自保");
        }

        // 如果是王+主组合，取主牌的花色
        if (currentLiangZhuInfo.getZhuCards().size() == 3) {
            // 如果有两个王+主，花色取主牌的花色
            currentLiangZhuColor = BasePoker.getCardColor(currentLiangZhuInfo.getZhuCards().get(2));  // 取主牌的花色
        }

        // 检查自反的牌是否和之前亮主的花色不同
        for (Integer card : cards) {
            int cardColor = BasePoker.getCardColor(card);
            if (cardColor == currentLiangZhuColor) {
                log.warn("[CardTypeChecker] 自反操作的牌花色与之前亮主相同，card={}, liangZhuColor={}",
                        toCardString(card), currentLiangZhuColor);
                throw new GameOpException(GameErrorCode.INVALID_CARD, "与当前亮主花色想通，自反失败");
            }
        }

        return true; // 合法
    }

    /**
     * 评估亮主牌的强度，依据规则：
     * 一张主 < 两张主 < 两个小王+主 < 两个大王+主
     */
    public static int evaluateLiangZhu(List<Integer> cards, GameSetCore set) {
        // 如果是空牌，直接返回最小值
        if (cards == null || cards.isEmpty()) {
            return 0;
        }

        int zhuValue = set.getZhuValue();
        int zhuColor = set.getLiangZhuInfo().getZhuColor();

        // 检查有几张主牌
        int mainCount = 0;
        int smallWangCount = 0;
        int bigWangCount = 0;

        for (Integer card : cards) {
            int cardValue = BasePoker.getCardValueEx(card);
            int cardColor = BasePoker.getCardColor(card);

            if (cardValue == zhuValue) {
                mainCount++;
            } else if (cardValue == 17) { // 小王
                smallWangCount++;
            } else if (cardValue == 18) { // 大王
                bigWangCount++;
            }
        }

        // 根据规则进行比较
        if (smallWangCount == 2 && mainCount == 1) {
            return 3; // 两个小王+主
        } else if (bigWangCount == 2 && mainCount == 1) {
            return 4; // 两个大王+主
        } else if (mainCount == 1) {
            return 1; // 一张主
        } else if (mainCount == 2) {
            return 2; // 两张主
        }

        return 0; // 无效牌型
    }

    // 校验出牌阶段的操作
    public boolean checkPlayingOp(GameSetPos pos, PKOpType opType, List<Integer> cards, GameSetCore set) {
        if (cards == null || cards.isEmpty()) {
            log.warn("[CardCheck] 玩家 {} 出牌为空", pos.getPlayerId());
            throw new GameOpException(GameErrorCode.INVALID_CARD, "不能出牌为空");
        }

        List<Integer> hand = new ArrayList<>(pos.getHandCards());

        log.info("[CardCheck] 玩家 {} 当前手牌：{}", pos.getPlayerId(), toCardString(hand));
        log.info("[CardCheck] 玩家 {} 请求出牌 opType={}，cards={}", pos.getPlayerId(), opType, toCardString(cards));

        // 检查玩家是否持有这些牌
        if (!hand.containsAll(cards)) {
            log.warn("[CardCheck] 玩家 {} 不持有所有出牌：{}", pos.getPlayerId(), cards);
            throw new GameOpException(GameErrorCode.INVALID_CARD, "不能出手中没有的牌");
        }

        // 获取首家出牌
        GameRoundCore round = set.getCurrentRound();
        int zhuValue = set.getZhuValue();
        int zhuColor = set.getLiangZhuInfo().getZhuColor();
        // 当前是首出，判断是否为合法牌型
        if (round == null || round.getRoundCards().isEmpty()) {
            log.info("[CardCheck] 玩家 {} 是本轮首出，需校验牌型", pos.getPlayerId());

            boolean isValidType = false;
            if (cards.size() == 1) {
                isValidType = true; // 单张直接合法
            } else if (isDui(cards)) {
                isValidType = true;
            } else if (isLianDui(cards, zhuColor, zhuValue)) {
                isValidType = true;
            }

            if (!isValidType) {
                log.warn("[CardCheck] 玩家 {} 首出牌型不合法，cards={}", pos.getPlayerId(), BasePoker.toCardString(cards));
                throw new GameOpException(GameErrorCode.INVALID_CARD, "首出必须是单张、对子或连对");
            }

            return true;
        }

        // 花色跟牌逻辑判断
        int firstCard = round.getFirstOutCards().get(0);
        int firstColor = BasePoker.getCardColor(firstCard);
        int firstValue = BasePoker.getCardValue(firstCard);
        log.info("[CardCheck] 本轮首牌：{}（花色={}）", BasePoker.getCardDescription(firstCard), firstColor);

        // 比较牌的数量
        if (cards.size() != round.getFirstOutCards().size()) {
            log.warn("[CardCheck] 本轮出牌失败，出牌数量与本回合首出数量不符，首出牌数量：{}，当前出牌数量：{}", round.getFirstOutCards().size(), cards.size());
            throw new GameOpException(GameErrorCode.INVALID_CARD, "出牌数量与本回合首出不符");
        }

        // 当前出牌的颜色
        for (int i = 0; i < cards.size(); i++) {
            int playCard = cards.get(i);
            int playColor = BasePoker.getCardColor(playCard);
            int playValue = BasePoker.getCardValue(playCard);
            log.info("[CardCheck] 玩家 {} 所出第{}张牌：{}（花色={}）", pos.getPlayerId(), i + 1, BasePoker.getCardDescription(playCard), playColor);

            // 玩家是否跟了不同花色（即"不跟")
            if (playColor != firstColor) {
                if (BasePoker.isZhuPai(firstCard, zhuColor, zhuValue)) {
                    // 👉 首出是主牌，需要玩家跟主牌；如果跟了副牌，需要确认手里确实没有主牌了
                    if (!BasePoker.isZhuPai(playCard, zhuColor, zhuValue)) {
                        List<Integer> remain = new ArrayList<>(hand);
                        BasePokerLogic.removeExactCards(remain, cards); // 剩余手牌

                        boolean hasZhu = remain.stream()
                                .anyMatch(c -> BasePoker.isZhuPai(c, zhuColor, zhuValue));

                        if (hasZhu) {
                            log.warn("[CardCheck] 玩家 {} 应出主牌，但仍有主牌却出了副牌，违规", pos.getPlayerId());
                            throw new GameOpException(GameErrorCode.INVALID_CARD, "应该出主牌，手里有主牌，但却出了副牌");
                        } else {
                            log.info("[CardCheck] 玩家 {} 无主牌可出，允许用副牌跟牌", pos.getPlayerId());
                        }
                    }
                }
                else {
                    // 如果首出是副牌，需要判断是否还有该花色的牌。
                    List<Integer> remain = new ArrayList<>(hand);
                    BasePokerLogic.removeExactCards(remain, cards);
                    boolean hasFirstColor = remain.stream()
                            .filter(c -> BasePoker.getCardValue(c) != zhuValue) // 排除主牌
                            .anyMatch(c -> BasePoker.getCardColor(c) == firstColor);

                    if (hasFirstColor) {
                        log.warn("[CardCheck] 玩家 {} 拥有花色 {} 的副牌，但未跟牌，违规", pos.getPlayerId(), firstColor);
                        throw new GameOpException(GameErrorCode.INVALID_CARD, "有相同花色的副牌，但却没跟该花色副牌");
                    } else {
                        log.info("[CardCheck] 玩家 {} 无法跟花色 {}，允许出其他花色", pos.getPlayerId(), firstColor);
                    }
                }
            }
            else {
                // 花色跟上了，再判断牌值是不是常主（value == zhuValue）
                if (playValue == zhuValue && !BasePoker.isZhuPai(firstCard, zhuColor, zhuValue)) {
                    // 如果此牌是常主（非主花色但值为zhuValue），则不能当作副牌来跟
                    List<Integer> remain = new ArrayList<>(hand);
                    BasePokerLogic.removeExactCards(remain, cards);

                    boolean hasPureVice = remain.stream()
                            .filter(c -> !BasePoker.isZhuPai(c, zhuColor, zhuValue)) // 非主
                            .anyMatch(c -> BasePoker.getCardColor(c) == firstColor);

                    if (hasPureVice) {
                        log.warn("[CardCheck] 玩家 {} 用常主（值={}）替代副牌 {} 跟牌，违规", pos.getPlayerId(), zhuValue, firstColor);
                        throw new GameOpException(GameErrorCode.INVALID_CARD, "不能使用常主当做副牌跟牌");
                    }
                } else if (!BasePoker.isZhuPai(playCard, zhuColor, zhuValue)
                        && BasePoker.getCardValue(firstCard) == zhuValue) {
                    // 👉 首出为常主（值为zhuValue但非主色），跟牌同花色但为副牌（非常主）
                    List<Integer> remain = new ArrayList<>(hand);
                    BasePokerLogic.removeExactCards(remain, cards);

                    boolean hasOtherZhu = remain.stream()
                            .anyMatch(c -> BasePoker.isZhuPai(c, zhuColor, zhuValue));

                    if (hasOtherZhu) {
                        log.warn("[CardCheck] 玩家 {} 首出是常主，跟的是副牌，但仍有其他主牌可出，违规", pos.getPlayerId());
                        throw new GameOpException(GameErrorCode.INVALID_CARD, "应该继续出主牌，不能用副牌跟常主");
                    }
                }
            }
        }

        return true;
    }

    /**
     * 评估当前出牌的综合比较值（越大越优）
     * 用于同一回合中比大小判断谁是赢家。
     */
    public CardPower evaluatePower(List<Integer> cards, GameRoundCore round) {
        GameSetCore set = round.getRoomSet();
        int zhuColor = set.getLiangZhuInfo().getZhuColor();
        int zhuValue = set.getZhuValue();

        if (cards == null || cards.isEmpty()) {
            log.info("[CardCheck] 出牌为空，返回最低牌力");
            return new CardPower(0, 0, 0, cards);
        }

        int cardType = getCardType(cards, zhuColor, zhuValue);
        int colorType = getColorType(cards.get(0), zhuColor, zhuValue);
        int max = cards.stream()
                .mapToInt(c -> getCardValue(c, zhuColor, zhuValue))
                .max().orElse(0);

        String desc = BasePoker.toCardString(cards);
        log.info("[CardCheck] 出牌评估：cards={}，牌型={}，花色类型={}，最大值={}",
                desc, cardType, colorType, max);

        return new CardPower(cardType, colorType, max, cards);
    }

    /**
     * 判断出牌花色类型：主牌=2，副牌=1，烂牌=0
     */
    private static int getColorType(int card, int zhuColor, int zhuValue) {
        boolean isZhu = BasePoker.isZhuPai(card, zhuColor, zhuValue);
        int result = isZhu ? 2 : 1;
        log.info("[ColorType] 牌 {} 是{}主牌", BasePoker.getCardDescription(card), isZhu ? "" : "非");
        return result;
    }

    /**
     * 判断一组牌的牌型：
     * 1 = 单张
     * 2 = 对子
     * 3 = 连对（拖拉机）
     * 4 = 甩牌（无法识别成其他牌型的集合）
     * 0 = 无效（空）
     */
    public static int getCardType(List<Integer> cards, int zhuColor, int zhuValue) {
        if (cards == null || cards.isEmpty()) {
            log.warn("[CardCheck] 空牌认定为无效牌型");
            return 0;
        }

        if (cards.size() == 1) {
            log.info("[CardCheck] 单张牌型");
            return 1;
        }

        if (cards.size() == 2 && isDui(cards)) {
            log.info("[CardCheck] 对子牌型");
            return 2;
        }

        if (isLianDui(cards, zhuColor, zhuValue)) {
            log.info("[CardCheck] 连对牌型");
            return 3;
        }

        log.info("[CardCheck] 默认甩牌类型");
        return 0;       // 设为最低
    }

    /**
     * 获取某张牌的最终比较值（考虑主、副、王等因素）
     */
    public static int getCardValue(int card, int zhuColor, int zhuValue) {
        if (isDaWang(card)) return 400;
        if (isXiaoWang(card)) return 390;

        int val = BasePoker.getCardValue(card);
        int color = BasePoker.getCardColor(card);

        boolean isZhuColor = color == zhuColor;
        boolean isZhuValue = val == zhuValue;

        if (isZhuValue && isZhuColor) {
            return 380 + val; // 主花色的常主牌，最强常主
        } else if (isZhuValue) {
            return 370 + val; // 其他花色的常主牌
        } else if (isZhuColor) {
            return 200 + val; // 主花色的非主点牌
        } else {
            return 100 + val; // 副牌
        }
    }

    private static boolean isSameValue(List<Integer> cards) {
        int val = BasePoker.getCardValue(cards.get(0));
        return cards.stream().allMatch(c -> BasePoker.getCardValue(c) == val);
    }

    private static boolean isDan(List<Integer> cardList) {
        return cardList.size() == 1;
    }

    public static boolean isDui(List<Integer> cardList) {
        if (cardList == null || cardList.size() != 2) {
            log.info("[CardCheck] 非对子，数量错误：{}", cardList != null ? cardList.size() : 0);
            return false;
        }

        int val1 = BasePoker.getCardValue(cardList.get(0));
        int val2 = BasePoker.getCardValue(cardList.get(1));
        int color1 = BasePoker.getCardColor(cardList.get(0));
        int color2 = BasePoker.getCardColor(cardList.get(1));

        boolean result = val1 == val2 && color1 == color2;

        log.info("[CardCheck] 判断对子：{} - {}，点数相同={}，花色相同={}，最终结果={}",
                BasePoker.getCardDescription(cardList.get(0)),
                BasePoker.getCardDescription(cardList.get(1)),
                val1 == val2,
                color1 == color2,
                result
        );

        return result;
    }

    public static boolean isLianDui(List<Integer> cardList, int zhuColor, int zhuValue) {
        log.info("[CardCheck] 判断是否连对：{}", BasePoker.toCardString(cardList));

        if (!checkAllTongHua(cardList, zhuColor, zhuValue)) {
            log.info("[CardCheck] 非连对：花色不一致");
            return false;
        }

        if (cardList.size() < 4 || cardList.size() % 2 != 0) {
            log.info("[CardCheck] 非连对：张数不满足，当前={}", cardList.size());
            return false;
        }

        // ✅ 新逻辑：统一调用新版连对提取逻辑（主副牌分堆 + 主2转换）
        List<List<Integer>> allLianDuis = BaseCardType.lianDuiList(cardList, zhuColor, zhuValue, -1);

        for (List<Integer> group : allLianDuis) {
            if (group.size() == cardList.size() && group.containsAll(cardList)) {
                log.info("[CardCheck] 确认为连对：{}", BasePoker.toCardString(group));
                return true;
            }
        }

        log.info("[CardCheck] 判断失败：不满足连对条件");
        return false;
    }

    private static boolean checkAllTongHua(List<Integer> cardList, int zhuColor, int zhuValue) {
        if (cardList == null || cardList.isEmpty()) return false;

        int firstColor = getColor(cardList.get(0), zhuColor, zhuValue);
        for (Integer card : cardList) {
            int color = getColor(card, zhuColor, zhuValue);
            if (color != firstColor) {
                log.info("[CardCheck] 非同花色：发现牌 {} 花色={} 与首牌花色 {} 不一致", BasePoker.getCardDescription(card), color, firstColor);
                return false;
            }
        }
        log.info("[CardCheck] 所有牌同花色：{}", cardList.stream().map(BasePoker::getCardDescription).toList());
        return true;
    }

    public static int getColor(int card, int zhuColor, int zhuValue) {
        int color = BasePoker.getCardColor(card);
        int cardValueEx = BasePoker.getCardValueEx(card);

        boolean isZhu = cardValueEx == zhuValue || color == 4;
        int finalColor = isZhu ? zhuColor : color;

        return finalColor;
    }

}

